home *** CD-ROM | disk | FTP | other *** search
/ MacHack 2000 / MacHack 2000.toast / pc / The Hacks / MacHacksBug / Python 1.5.2c1 / Extensions / Imaging / PIL / ArgImagePlugin.py < prev    next >
Encoding:
Python Source  |  2000-06-23  |  11.6 KB  |  583 lines

  1. #
  2. # THIS IS WORK IN PROGRESS
  3. #
  4. # The Python Imaging Library.
  5. # $Id: ArgImagePlugin.py,v 1.1.1.2 1999/01/13 09:39:51 sjoerd Exp $
  6. #
  7. # ARG animation support code
  8. #
  9. # history:
  10. #    96-12-30 fl    Created
  11. #    96-01-06 fl    Added safe scripting environment
  12. #    96-01-10 fl    Added JHDR, UHDR and sYNC support
  13. #
  14. # Copyright (c) Secret Labs AB 1997.
  15. # Copyright (c) Fredrik Lundh 1996-97.
  16. #
  17. # See the README file for information on usage and redistribution.
  18. #
  19.  
  20. __version__ = "0.3"
  21.  
  22. import marshal, rexec, string
  23.  
  24. import Image, ImageFile, ImagePalette
  25.  
  26. from PngImagePlugin import i16, i32, ChunkStream, _MODES
  27.  
  28. MAGIC = "\212ARG\r\n\032\n"
  29.  
  30. APPLET_HOOK = None # must be explicitly enabled to support embedded scripts
  31.  
  32. # --------------------------------------------------------------------
  33. # ARG parser
  34.  
  35. class ArgStream(ChunkStream):
  36.     "Parser callbacks for ARG data"
  37.  
  38.     def __init__(self, fp):
  39.  
  40.     ChunkStream.__init__(self, fp)
  41.  
  42.     self.eof = 0
  43.  
  44.     self.im = None
  45.     self.palette = None
  46.  
  47.     self.__reset()
  48.  
  49.     def __reset(self):
  50.  
  51.     # reset decoder state (called on init and sync)
  52.  
  53.     self.count = 0
  54.     self.id = None
  55.     self.action = ("NONE",)
  56.  
  57.     self.images = {}
  58.     self.names = {}
  59.  
  60.     self.applets = {} # level 2
  61.  
  62.  
  63.     def chunk_AHDR(self, offset, bytes):
  64.     "AHDR -- animation header"
  65.  
  66.     # assertions
  67.     if self.count != 0:
  68.         raise SyntaxError, "misplaced AHDR chunk"
  69.  
  70.     s = self.fp.read(bytes)
  71.     self.size = i32(s), i32(s[4:])
  72.     try:
  73.         self.mode, self.rawmode = _MODES[(ord(s[8]), ord(s[9]))]
  74.     except:
  75.         raise SyntaxError, "unknown ARG mode"
  76.  
  77.     if Image.DEBUG:
  78.         print "AHDR size", self.size
  79.         print "AHDR mode", self.mode, self.rawmode
  80.  
  81.     return s
  82.  
  83.     def chunk_AFRM(self, offset, bytes):
  84.     "AFRM -- next frame follows"
  85.  
  86.     # assertions
  87.     if self.count != 0:
  88.         raise SyntaxError, "misplaced AFRM chunk"
  89.  
  90.     self.show = 1
  91.     self.id = 0
  92.     self.count = 1
  93.     self.repair = None
  94.  
  95.     s = self.fp.read(bytes)
  96.     if len(s) >= 2:
  97.         self.id = i16(s)
  98.         if len(s) >= 4:
  99.         self.count = i16(s[2:4])
  100.         if len(s) >= 6:
  101.             self.repair = i16(s[4:6])
  102.         else:
  103.             self.repair = None
  104.  
  105.     if Image.DEBUG:
  106.         print "AFRM", self.id, self.count
  107.  
  108.     return s
  109.  
  110.     def chunk_ADEF(self, offset, bytes):
  111.     "ADEF -- store image"
  112.  
  113.     # assertions
  114.     if self.count != 0:
  115.         raise SyntaxError, "misplaced ADEF chunk"
  116.  
  117.     self.show = 0
  118.     self.id = 0
  119.     self.count = 1
  120.     self.repair = None
  121.  
  122.     s = self.fp.read(bytes)
  123.     if len(s) >= 2:
  124.         self.id = i16(s)
  125.         if len(s) >= 4:
  126.         self.count = i16(s[2:4])
  127.  
  128.     if Image.DEBUG:
  129.         print "ADEF", self.id, self.count
  130.  
  131.     return s
  132.  
  133.     def chunk_NAME(self, offset, bytes):
  134.     "NAME -- name the current image"
  135.  
  136.     # assertions
  137.     if self.count == 0:
  138.         raise SyntaxError, "misplaced NAME chunk"
  139.  
  140.     name = self.fp.read(bytes)
  141.     self.names[self.id] = name
  142.  
  143.     return name
  144.  
  145.     def chunk_AEND(self, offset, bytes):
  146.     "AEND -- end of animation"
  147.  
  148.     if Image.DEBUG:
  149.         print "AEND"
  150.  
  151.     self.eof = 1
  152.  
  153.     raise EOFError, "end of ARG file"
  154.  
  155.     def __getmodesize(self, s, full=1):
  156.  
  157.     size = i32(s), i32(s[4:])
  158.  
  159.     try:
  160.         mode, rawmode = _MODES[(ord(s[8]), ord(s[9]))]
  161.     except:
  162.         raise SyntaxError, "unknown image mode"
  163.  
  164.     if full:
  165.         if ord(s[12]):
  166.         pass # interlace not yet supported
  167.         if ord(s[11]):
  168.         raise SyntaxError, "unknown filter category"
  169.  
  170.     return size, mode, rawmode
  171.  
  172.     def chunk_PAST(self, offset, bytes):
  173.     "PAST -- paste one image into another"
  174.  
  175.     # assertions
  176.     if self.count == 0:
  177.         raise SyntaxError, "misplaced PAST chunk"
  178.  
  179.     if self.repair is not None:
  180.         # we must repair the target image before we
  181.         # start pasting
  182.  
  183.         # brute force; a better solution would be to
  184.         # update only the dirty rectangles in images[id].
  185.         # note that if images[id] doesn't exist, it must
  186.         # be created
  187.  
  188.         self.images[self.id] = self.images[self.repair].copy()
  189.         self.repair = None
  190.  
  191.     s = self.fp.read(bytes)
  192.     im = self.images[i16(s)]
  193.     x, y = i32(s[2:6]), i32(s[6:10])
  194.     bbox = x, y, im.size[0]+x, im.size[1]+y
  195.  
  196.     if im.mode in ["RGBA"]:
  197.         # paste with transparency
  198.         # FIXME: should handle P+transparency as well
  199.         self.images[self.id].paste(im, bbox, im)
  200.     else:
  201.         # paste without transparency
  202.         self.images[self.id].paste(im, bbox)
  203.  
  204.     self.action = ("PAST",)
  205.     self.__store()
  206.  
  207.     return s
  208.  
  209.     def chunk_BLNK(self, offset, bytes):
  210.     "BLNK -- create blank image"
  211.  
  212.     # assertions
  213.     if self.count == 0:
  214.         raise SyntaxError, "misplaced BLNK chunk"
  215.  
  216.     s = self.fp.read(bytes)
  217.     size, mode, rawmode = self.__getmodesize(s, 0)
  218.  
  219.     # store image (FIXME: handle colour)
  220.     self.action = ("BLNK",)
  221.     self.im = Image.core.fill(mode, size, 0)
  222.     self.__store()
  223.  
  224.     return s
  225.  
  226.     def chunk_IHDR(self, offset, bytes):
  227.     "IHDR -- full image follows"
  228.  
  229.     # assertions
  230.     if self.count == 0:
  231.         raise SyntaxError, "misplaced IHDR chunk"
  232.  
  233.     # image header
  234.     s = self.fp.read(bytes)
  235.     size, mode, rawmode = self.__getmodesize(s)
  236.  
  237.     # decode and store image
  238.     self.action = ("IHDR",)
  239.     self.im = Image.core.new(mode, size)
  240.     self.decoder = Image.core.zip_decoder(rawmode)
  241.     self.decoder.setimage(self.im, (0,0) + size)
  242.     self.data = ""
  243.  
  244.     return s
  245.  
  246.     def chunk_DHDR(self, offset, bytes):
  247.     "DHDR -- delta image follows"
  248.  
  249.     # assertions
  250.     if self.count == 0:
  251.         raise SyntaxError, "misplaced DHDR chunk"
  252.  
  253.     s = self.fp.read(bytes)
  254.  
  255.     size, mode, rawmode = self.__getmodesize(s)
  256.  
  257.     # delta header
  258.     diff = ord(s[13])
  259.     offs = i32(s[14:18]), i32(s[18:22])
  260.  
  261.     bbox = offs + (offs[0]+size[0], offs[1]+size[1])
  262.  
  263.     if Image.DEBUG:
  264.         print "DHDR", diff, bbox
  265.  
  266.     # FIXME: decode and apply image
  267.     self.action = ("DHDR", diff, bbox)
  268.  
  269.     # setup decoder
  270.     self.im = Image.core.new(mode, size)
  271.  
  272.     self.decoder = Image.core.zip_decoder(rawmode)
  273.     self.decoder.setimage(self.im, (0,0) + size)
  274.  
  275.     self.data = ""
  276.  
  277.     return s
  278.  
  279.     def chunk_JHDR(self, offset, bytes):
  280.     "JHDR -- JPEG image follows"
  281.  
  282.     # assertions
  283.     if self.count == 0:
  284.         raise SyntaxError, "misplaced JHDR chunk"
  285.  
  286.     # image header
  287.     s = self.fp.read(bytes)
  288.     size, mode, rawmode = self.__getmodesize(s, 0)
  289.  
  290.     # decode and store image
  291.     self.action = ("JHDR",)
  292.     self.im = Image.core.new(mode, size)
  293.     self.decoder = Image.core.jpeg_decoder(rawmode)
  294.     self.decoder.setimage(self.im, (0,0) + size)
  295.     self.data = ""
  296.  
  297.     return s
  298.  
  299.     def chunk_UHDR(self, offset, bytes):
  300.     "UHDR -- uncompressed image data follows (EXPERIMENTAL)"
  301.  
  302.     # assertions
  303.     if self.count == 0:
  304.         raise SyntaxError, "misplaced UHDR chunk"
  305.  
  306.     # image header
  307.     s = self.fp.read(bytes)
  308.     size, mode, rawmode = self.__getmodesize(s, 0)
  309.  
  310.     # decode and store image
  311.     self.action = ("UHDR",)
  312.     self.im = Image.core.new(mode, size)
  313.     self.decoder = Image.core.raw_decoder(rawmode)
  314.     self.decoder.setimage(self.im, (0,0) + size)
  315.     self.data = ""
  316.  
  317.     return s
  318.  
  319.     def chunk_IDAT(self, offset, bytes):
  320.     "IDAT -- image data block"
  321.  
  322.     # pass compressed chunks through the decoder
  323.     s = self.fp.read(bytes)
  324.     self.data = self.data + s
  325.     n, e = self.decoder.decode(self.data)
  326.     if n < 0:
  327.         # end of image
  328.         if e < 0:
  329.         raise IOError, "decoder error %d" % e
  330.     else:
  331.         self.data = self.data[n:]
  332.  
  333.     return s
  334.  
  335.     def chunk_DEND(self, offset, bytes):
  336.     return self.chunk_IEND(offset, bytes)
  337.  
  338.     def chunk_JEND(self, offset, bytes):
  339.     return self.chunk_IEND(offset, bytes)
  340.  
  341.     def chunk_UEND(self, offset, bytes):
  342.     return self.chunk_IEND(offset, bytes)
  343.  
  344.     def chunk_IEND(self, offset, bytes):
  345.     "IEND -- end of image"
  346.  
  347.     # we now have a new image.  carry out the operation
  348.     # defined by the image header.
  349.  
  350.     # won't need these anymore
  351.     del self.decoder
  352.     del self.data
  353.  
  354.     self.__store()
  355.  
  356.     return self.fp.read(bytes)
  357.  
  358.     def __store(self):
  359.  
  360.     # apply operation
  361.     cid = self.action[0]
  362.  
  363.     if cid in ["BLNK", "IHDR", "JHDR", "UHDR"]:
  364.         # store
  365.         self.images[self.id] = self.im
  366.  
  367.     elif cid == "DHDR":
  368.         # paste
  369.         cid, mode, bbox = self.action
  370.         im0 = self.images[self.id]
  371.         im1 = self.im
  372.         if mode == 0:
  373.         im1 = im1.chop_add_modulo(im0.crop(bbox))
  374.         im0.paste(im1, bbox)
  375.  
  376.     self.count = self.count - 1
  377.  
  378.     if self.count == 0 and self.show:
  379.         self.im = self.images[self.id]
  380.         raise EOFError # end of this frame
  381.  
  382.     def chunk_PLTE(self, offset, bytes):
  383.     "PLTE -- palette data"
  384.  
  385.     s = self.fp.read(bytes)
  386.     if self.mode == "P":
  387.         self.palette = ImagePalette.raw("RGB", s)
  388.     return s
  389.  
  390.     def chunk_sYNC(self, offset, bytes):
  391.     "SYNC -- reset decoder"
  392.  
  393.     if self.count != 0:
  394.         raise SyntaxError, "misplaced sYNC chunk"
  395.  
  396.     s = self.fp.read(bytes)
  397.     self.__reset()
  398.     return s
  399.  
  400.     #
  401.     # LEVEL 2 STUFF
  402.  
  403.     def chunk_aAPP(self, offset, bytes):
  404.     "aAPP -- store application"
  405.  
  406.     s = self.fp.read(bytes)
  407.  
  408.     # extract type, name and code chunk
  409.     j = string.find(s, "\0")
  410.     name = s[:j]
  411.  
  412.     i = j + 1
  413.     j = string.find(s, "\0", i)
  414.     type = s[i:j]
  415.  
  416.     code = s[j+1:]
  417.  
  418.     if Image.DEBUG:
  419.         print "AAPP", repr(type), repr(name)
  420.  
  421.     if not code:
  422.         # delete existing applet
  423.         if self.applets.has_key(name):
  424.         del self.applets[name]
  425.     else:
  426.         # store or execute applet
  427.         if type != "python":
  428.         raise IOError, "unsupported script type " + type
  429.         # convert to executable object
  430.         code = marshal.loads(code)
  431.         if not name:
  432.         # unnamed; execute immediately
  433.         self.__applet(code)
  434.         else:
  435.         try:
  436.             # named applet; store in dictionary
  437.             self.applets[name] = marshal.loads(code)
  438.         except:
  439.             pass # applet loading error
  440.  
  441.     return s
  442.  
  443.     def chunk_aRUN(self, offset, bytes):
  444.     "aRUN -- execute application"
  445.  
  446.     s = self.fp.read(bytes)
  447.  
  448.     j = string.find(s, "\0")
  449.     name = s[:j]
  450.  
  451.     # FIXME: should handle arguments
  452.     print "ARUN", name
  453.  
  454.     self.__applet(self.applets[name])
  455.  
  456.     return s
  457.  
  458.     def __applet(self, code):
  459.  
  460.     if not APPLET_HOOK:
  461.         return
  462.  
  463.     # run script in safe environment
  464.     safe = rexec.RExec()
  465.     safe.r_exec(code)
  466.  
  467.     # must convert images to Image object form
  468.     images = {}
  469.     for id, im in self.images.items():
  470.         # FIXME: this is crude: support for this operation
  471.         # should be moved to the Image module itself (or
  472.         # better; change this module to use Image objects
  473.         # instead of core objects)
  474.         i = Image.new(im.mode, im.size)
  475.         i.im = im
  476.         images[id] = i
  477.         if self.names.has_key(id):
  478.         # add named image
  479.         images[self.names[id]] = i
  480.  
  481.     APPLET_HOOK(safe.modules["__main__"].Animation, images)
  482.  
  483.  
  484. # --------------------------------------------------------------------
  485. # ARG reader
  486.  
  487. def _accept(prefix):
  488.     return prefix[:8] == MAGIC
  489.  
  490.  
  491. class ArgImageFile(ImageFile.ImageFile):
  492.  
  493.     format = "ARG"
  494.     format_description = "Animated raster graphics"
  495.  
  496.     def _open(self):
  497.  
  498.     if self.fp.read(8) != MAGIC:
  499.         raise SyntaxError, "not an ARG file"
  500.  
  501.     self.arg = ArgStream(self.fp)
  502.  
  503.     # read and process the first chunk (AHDR)
  504.  
  505.     cid, offset, bytes = self.arg.read()
  506.  
  507.     if cid != "AHDR":
  508.         raise SyntaxError, "expected an AHDR chunk"
  509.  
  510.     s = self.arg.call(cid, offset, bytes)
  511.  
  512.     self.arg.crc(cid, s)
  513.  
  514.     # image characteristics
  515.     self.mode = self.arg.mode
  516.     self.size = self.arg.size
  517.  
  518.     def load(self, modify=0):
  519.  
  520.     if self.arg.im is None:
  521.         self.seek(0)
  522.  
  523.     # image data
  524.     self.im = self.arg.im
  525.     self.palette = self.arg.palette
  526.  
  527.     # set things up for further processing
  528.     Image.Image.load(self, modify)
  529.  
  530.     def seek(self, frame):
  531.  
  532.     if self.arg.eof:
  533.         raise EOFError, "end of animation"
  534.  
  535.     self.fp = self.arg.fp
  536.  
  537.     while 1:
  538.  
  539.         #
  540.         # process chunks
  541.  
  542.         cid, offset, bytes = self.arg.read()
  543.  
  544.         if self.arg.eof:
  545.         raise EOFError, "end of animation"
  546.  
  547.         try:
  548.         s = self.arg.call(cid, offset, bytes)
  549.         except EOFError:
  550.         break
  551.  
  552.         except "glurk": # AttributeError
  553.         if Image.DEBUG:
  554.             print cid, bytes, "(unknown)"
  555.         s = self.fp.read(bytes)
  556.  
  557.         self.arg.crc(cid, s)
  558.  
  559.     self.fp.read(4) # ship extra CRC
  560.  
  561.     def tell(self):
  562.     return 0
  563.  
  564.     def verify(self):
  565.     "Verify ARG file"
  566.  
  567.     # back up to first chunk
  568.     self.fp.seek(8)
  569.  
  570.     self.arg.verify(self)
  571.     self.arg.close()
  572.  
  573.     self.fp = None
  574.  
  575. #
  576. # --------------------------------------------------------------------
  577.  
  578. Image.register_open("ARG", ArgImageFile, _accept)
  579.  
  580. Image.register_extension("ARG", ".arg")
  581.  
  582. Image.register_mime("ARG", "video/x-arg")
  583.